/**
 * 斗地主分析器
 */

import { CARD, PokerCard } from "../poker/card";
import { PokerSet } from "../poker/set";
import { LANDLORD_TYPE, SetInfo } from "./setInfo";

export class landLordChecker {

  private dic: Map<number, PokerSet>;

  constructor() {
    this.dic = new Map<number, PokerSet>();
    this.dic.set(CARD.VALUE.THREE, new PokerSet());
    this.dic.set(CARD.VALUE.FOUR, new PokerSet());
    this.dic.set(CARD.VALUE.FIVE, new PokerSet());
    this.dic.set(CARD.VALUE.SIX, new PokerSet());
    this.dic.set(CARD.VALUE.SEVEN, new PokerSet());
    this.dic.set(CARD.VALUE.EIGHT, new PokerSet());
    this.dic.set(CARD.VALUE.NINE, new PokerSet());
    this.dic.set(CARD.VALUE.TEN, new PokerSet());
    this.dic.set(CARD.VALUE.JACK, new PokerSet());
    this.dic.set(CARD.VALUE.QUEEN, new PokerSet());
    this.dic.set(CARD.VALUE.KING, new PokerSet());
    this.dic.set(CARD.VALUE.ACE, new PokerSet());
    this.dic.set(CARD.VALUE.TWO, new PokerSet());
    this.dic.set(CARD.VALUE.BLACK_JOKER, new PokerSet());
    this.dic.set(CARD.VALUE.RED_JOKER, new PokerSet());
  }

  /**
   * 根据给定的扑克集更新记牌器, 出牌时调用
   * @param pokers
   * @returns
   */
  public RemovePokerSet(pokers: PokerSet) {
    pokers.DoOnEachPokerCard((index: number, card: PokerCard) => {
      this.dic.get(card.GetValue())?.DeletePokerByCard(card);
    })
  }

  public AddPokerSet(pokers: PokerSet) {
    pokers.DoOnEachPokerCard((index: number, card: PokerCard) => {
      this.dic.get(card.GetValue())?.AddPoker(card);
    })
  }

  public GetMinPlayableCards(): PokerSet {
    for (let i = CARD.VALUE.THREE; i <= CARD.VALUE.RED_JOKER; i++) {
      const set = this.dic.get(i);
      if (!!set && set.CountCards() > 0) {
        return set;
      }
    }
    return new PokerSet();
  }

  /**
   * 根据最后一次出牌的牌型信息, 返回可出的扑克集
   * @param setType
   * @returns
   */
  public GetUseableCards(setType: SetInfo): Array<PokerSet> {
    let useableSets = new Array<PokerSet>();

    switch (setType.setType) {
      case LANDLORD_TYPE.SINGLE: useableSets = this.getSingleValueSet(1, setType.GetMinValue()); break;
      case LANDLORD_TYPE.DRAGON: useableSets = this.getMultiValueSet(1, setType.GetMinValue(), setType.GetMaxValue()); break;
      case LANDLORD_TYPE.PAIR: useableSets = this.getSingleValueSet(2, setType.GetMinValue()); break;
      case LANDLORD_TYPE.MULIT_PAIRS: useableSets = this.getMultiValueSet(2, setType.GetMinValue(), setType.GetMaxValue()); break;
      case LANDLORD_TYPE.THREE: useableSets = this.getSingleValueSet(3, setType.GetMinValue()); break;
      case LANDLORD_TYPE.THREE_PLUS_ONE:
        useableSets = this.getSingleValueSet(3, setType.GetMinValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(1, 1, tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.THREE_PLUS_TWO:
        useableSets = this.getSingleValueSet(3, setType.GetMinValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(2, 1, tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.MULITY_THREE: useableSets = this.getMultiValueSet(3, setType.GetMinValue(), setType.GetMaxValue()); break;
      case LANDLORD_TYPE.MULITY_THREE_PLUS_ONE:
        useableSets = this.getMultiValueSet(3, setType.GetMinValue(), setType.GetMaxValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(1, setType.GetRangeWidth(), tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.MULITY_THREE_PLUS_TWO:
        useableSets = this.getMultiValueSet(3, setType.GetMinValue(), setType.GetMaxValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(2, setType.GetRangeWidth(), tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.FOUR_PLUS_TWO:
        useableSets = this.getSingleValueSet(4, setType.GetMinValue());
        for (const [i, tempset] of useableSets.entries()) {
          // 带两个单牌
          let tempsetPlus = this.getPlusSet(1, 2, tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 带一对牌, 看做两个单牌
            tempsetPlus = this.getPlusSet(2, 1, tempset);
            if (tempsetPlus.CountCards() > 0) {
              tempset.AddPokers(tempsetPlus.Pokers);
              useableSets[i] = tempset;
            } else {
              // 没有牌可以带, 将之前的主牌移除可出牌集合
              useableSets[i] = null as any;
            }
          }
        }
        break;
      case LANDLORD_TYPE.FOUR_PLUS_FOUR:
        useableSets = this.getSingleValueSet(4, setType.GetMinValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(2, 2, tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.MULITY_FOUR: useableSets = this.getMultiValueSet(4, setType.GetMinValue(), setType.GetMaxValue()); break;
      case LANDLORD_TYPE.MULITY_FOUR_PLUS_TWO:
        useableSets = this.getMultiValueSet(4, setType.GetMinValue(), setType.GetMaxValue());
        for (const [i, tempset] of useableSets.entries()) {
          // 带两个单牌
          let tempsetPlus = this.getPlusSet(1, 2 * setType.GetRangeWidth(), tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 带一对牌, 看做两个单牌
            tempsetPlus = this.getPlusSet(2, setType.GetRangeWidth(), tempset);
            if (tempsetPlus.CountCards() > 0) {
              tempset.AddPokers(tempsetPlus.Pokers);
              useableSets[i] = tempset;
            } else {
              // 没有牌可以带, 将之前的主牌移除可出牌集合
              useableSets[i] = null as any;
            }
          }
        }
        break;
      case LANDLORD_TYPE.MULITY_FOUR_PLUS_FOUR:
        useableSets = this.getMultiValueSet(4, setType.GetMinValue(), setType.GetMaxValue());
        for (const [i, tempset] of useableSets.entries()) {
          const tempsetPlus = this.getPlusSet(2, 2 * setType.GetRangeWidth(), tempset);
          if (tempsetPlus.CountCards() > 0) {
            tempset.AddPokers(tempsetPlus.Pokers);
            useableSets[i] = tempset;
          } else {
            // 没有牌可以带, 将之前的主牌移除可出牌集合
            useableSets[i] = null as any;
          }
        }
        break;
      case LANDLORD_TYPE.COMMON_BOMB: useableSets = this.getSingleValueSet(4, setType.GetMinValue()); break;
      case LANDLORD_TYPE.JOKER_BOMB: useableSets = new Array<PokerSet>(); break;
      default: useableSets = new Array<PokerSet>(); break;
    }
    // 去掉null元素
    let newUseableSets = new Array<PokerSet>();
    for (const sets of useableSets.values()) {
      if (!!sets) {
        newUseableSets.push(sets);
      }
    }
    // 上一次出牌不是炸弹，则直接将炸弹加入可出的排中
    if (setType.setType != LANDLORD_TYPE.COMMON_BOMB && setType.setType != LANDLORD_TYPE.JOKER_BOMB) {
      // 王炸
      const jokerBombSet = this.getJokerBomb();
      if (jokerBombSet.CountCards() > 0) {
        newUseableSets.push(jokerBombSet);
      }
      // 普通炸弹
      const bombSet = this.getSingleValueSet(4, -1);
      for (const tempSet of bombSet.values()) {
        if (tempSet.CountCards() > 0) {
          newUseableSets.push(tempSet);
        }
      }
    }
    return useableSets;
  }

  /**
   * 获取单值牌组成的扑克集的切片, 单排对牌三牌四排等等
   * @param count 单值牌的张数
   * @param minValue 上家出牌的最小的牌的大小
   * @returns
   */
  private getSingleValueSet(count: number, minValue: number): Array<PokerSet> {
    let sets = new Array<PokerSet>();
    // 先不拆牌的情况下查找
    for (let i = minValue + 1; i < CARD.VALUE.RED_JOKER; i++) {
      let setTemp = this.dic.get(i);
      if (!!setTemp && setTemp.CountCards() == count) {
        let set = new PokerSet();
        set.AddPokers(setTemp.Pokers);
        sets.push(set);
      }
    }
    // 不拆牌的情况下找不到可出的牌，再考虑拆牌的情况
    if (sets.length == 0) {
      for (let i = minValue + 1; i < CARD.VALUE.RED_JOKER; i++) {
        let setTemp = this.dic.get(i);
        if (!!setTemp && setTemp.CountCards() > count) {
          let set = new PokerSet();
          set.AddPokers(setTemp.Pokers);
          sets.push(set);
        }
      }
    }
    return sets;
  }

  /**
   * 获取多种不同值组成的扑克集的切片, 2连3连4连5连等
   * @param count
   * @param minValue
   * @param maxValue
   * @returns
   */
  private getMultiValueSet(count: number, minValue: number, maxValue: number): Array<PokerSet> {
    let sets = new Array<PokerSet>();
    const valueRange = maxValue - minValue + 1;
    // 先不拆牌的情况下查找
    for (let i = minValue + 1; i < CARD.VALUE.TWO - valueRange; i++) {
      let set = new PokerSet();
      let setTemp: PokerSet | undefined = undefined;
      for (let j = i; j < i + valueRange; j++) {
        setTemp = this.dic.get(j);
        if (!!setTemp && setTemp.CountCards() == count) {
          set = new PokerSet();
          set.AddPokers(setTemp.Pokers);
        }
      }
      // 该范围内连续的牌的张数符合要求
      if (!!setTemp && setTemp.CountCards() == valueRange * count) {
        sets.push(set);
      } else {
        set = new PokerSet();
      }
    }
    // 如果不拆拍找不到可出的牌, 则考虑拆牌
    if (sets.length == 0) {
      for (let i = minValue + 1; i < CARD.VALUE.TWO - valueRange; i++) {
        let set = new PokerSet();
        let setTemp: PokerSet | undefined = undefined;
        for (let j = i; j < i + valueRange; j++) {
          setTemp = this.dic.get(j);
          if (!!setTemp && setTemp.CountCards() > count) {
            set = new PokerSet();
            set.AddPokers(setTemp.Pokers);
            //se = se.AddPokers(ana.dic[j][:count])
          }
        }
        // 该范围内连续的牌的张数符合要求
        if (!!setTemp && setTemp.CountCards() == valueRange * count) {
          sets.push(set);
        } else {
          set = new PokerSet();
        }
      }
    }
    return sets;
  }

  /**
   * 获取附牌, 比如三带一中的一, 四带二中二, 只获取一种可能即可
   * 不拆牌为第一原则, 可能会带出去大牌
   * @param num 张数
   * @param count 系列数
   * @param exceptSet 不能包含在内的扑克集
   * @returns
   */
  private getPlusSet(num: number, count: number, exceptSet: PokerSet): PokerSet {
    let resSet = new PokerSet();
    // 第一原则不拆牌原则
    for (let i = CARD.VALUE.THREE; i <= CARD.VALUE.RED_JOKER; i++) {
      let setTemp = this.dic.get(i);
      if (!!setTemp && setTemp.CountCards() == num && !setTemp.HasSameValueCard(exceptSet)) {
        resSet.AddPokers(setTemp.Pokers);
      }
      if (resSet.CountCards() == num * count) {
        return resSet;
      }
    }
    // 不拆牌找不到则, 考虑拆牌
    if (resSet.CountCards() == 0) {
      for (let i = CARD.VALUE.THREE; i <= CARD.VALUE.RED_JOKER; i++) {
        let setTemp = this.dic.get(i);
        if (!!setTemp && setTemp.CountCards() > num && !setTemp.HasSameValueCard(exceptSet)) {
          resSet.AddPokers(setTemp.Pokers);
        }
        if (resSet.CountCards() == num * count) {
          return resSet;
        }
      }
    }
    return new PokerSet();
  }

  /**
   * 王炸
   * @returns
   */
  private getJokerBomb(): PokerSet {
    let resSet = new PokerSet();
    for (let i = CARD.VALUE.BLACK_JOKER; i <= CARD.VALUE.RED_JOKER; i++) {
      let setTemp = this.dic.get(i);
      if (!!setTemp && setTemp.CountCards() > 0) {
        resSet.AddPokers(setTemp.Pokers);
      }
    }
    if (resSet.CountCards() > 1) {
      return resSet;
    } else {
      return new PokerSet();
    }
  }
}
